package com.fulu.netty.luban.onereactor;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.util.Iterator;
import java.util.Set;

/**
 * 事件轮训，事件分发类
 */
public class TCPReactor implements Runnable {

    private final ServerSocketChannel ssc;

    private final Selector selector;

    public TCPReactor(int port) throws IOException {
        this.ssc = ServerSocketChannel.open(); // 打开服务端socket
        this.selector = Selector.open(); // 创建选择器对象
        InetSocketAddress addr = new InetSocketAddress(port);
        ssc.socket().bind(addr);
        ssc.configureBlocking(false);
        SelectionKey key = ssc.register(selector, SelectionKey.OP_ACCEPT);
        key.attach(new Accpetor(ssc, selector));
    }

    @Override
    public void run() {
        while (!Thread.interrupted()) { // 在线程中断前持续执行
            System.out.println("Waiting for new event on port: " + ssc.socket().getLocalPort() + "...");
            try {
                //查看是否有事件发生  有事件发生则把这个事件加入到selectedKeys
                selector.select();
            } catch (IOException e) {
                e.printStackTrace();
            }
            System.out.println("aaaaaaa");
            //能执行到这里  说明发生了事件
            // 取得所有已就绪事件的key集合
            Set<SelectionKey> selectedKeys = selector.selectedKeys();
            Iterator<SelectionKey> it = selectedKeys.iterator();
            //遍历已经发生的事件
            while (it.hasNext()) {
                SelectionKey selectionKey = it.next();  //read的事件对象
                // 根据事件的key进行调度
                dispatch(selectionKey);
                it.remove();
            }
        }
    }

    private void dispatch(SelectionKey key) {
        //取出附加对象  根据附加对象判断是什么事件 调用不同附加对象的run方法   这里请注意这个附加对象是会变化的
        //如果是接受客户端连接事件的话  这里取出的附加对象就是Acceptor,调用的肯定也是Acceptor的run方法
        //如果是已经连接上来的客户端发来数据  则是读事件  那么这里取出的附加对象就是TCPHandler，调用的肯定也是TCPHandler的run方法
        //TCPHandler那里来的请往下看
        //new Acceptor(selector, ssc).run()
        Runnable r = (Runnable) (key.attachment());  //取出附加对象
        if (r != null)
            r.run();
    }
}
